package com.wenlin.myproduction.widget;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import androidx.annotation.ColorInt;
import androidx.annotation.Nullable;

import com.wenlin.myproduction.R;
import com.wenlin.myproduction.bean.Airplane;
import com.wenlin.myproduction.bean.Block;
import com.wenlin.myproduction.util.Checker;
import com.wenlin.myproduction.util.LogUtils;
import com.wenlin.myproduction.util.ToastUtils;
import com.wenlin.myproduction.util.UI;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * 炸飞机游戏
 * 可以编辑三架飞机的位置，编辑之后将手机交给玩家。玩家点击格子进行轰炸。没炸到为“空”，炸到机身为“伤”，炸到机头为“沉”。三架飞机皆沉则胜利。
 */
public class BombView extends View {
    private static final String[] rows = {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J"};//行标
    private static final int DIRECTION_LEFT = 0;
    private static final int DIRECTION_TOP = 1;
    private static final int DIRECTION_RIGHT = 2;
    private static final int DIRECTION_DOWN = 3;
    private static final int[] directions = {DIRECTION_LEFT, DIRECTION_TOP, DIRECTION_RIGHT, DIRECTION_DOWN};//飞机的四个方向，分别是左上右下
    private Paint paint;
    private TextPaint textPaint;
    private Rect rect;
    private float blockLong;//每个格子的长度
    private List<Airplane> listAirplanes;
    private Random random;
    private Block[][] arrBlocks;//二维数组存放格子
    @ColorInt
    private int colorWhite = UI.getColor(R.color.myWhite);//白色
    @ColorInt
    private int colorBlack = UI.getColor(R.color.myTitle);//黑色
    @ColorInt
    private int colorGray = UI.getColor(R.color.myGray);//灰色
    @ColorInt
    private int colorAirplane1 = Color.parseColor("#90FF9999");//飞机1的颜色，偏红色
    @ColorInt
    private int colorAirplane2 = Color.parseColor("#9099FF99");//飞机2的颜色，偏绿色
    @ColorInt
    private int colorAirplane3 = Color.parseColor("#909999FF");//飞机3的颜色，偏蓝色
    @ColorInt
    private int colorDead = UI.getColor(R.color.myWhite);//击沉的格子的颜色
    @ColorInt
    private int colorHurt = UI.getColor(R.color.myGray);//击伤的格子的颜色
    @ColorInt
    private int colorNull = UI.getColor(R.color.myWhite);//空白格的颜色
    private int step;//步数
    private int countCreate;//设置飞机位置时调取生成飞机的的方法次数
    private int countDead;//炸毁的飞机数量
    private boolean editMode;
    //20220315 增加设置飞机功能，设置时默认随机摆放，可以点击三架飞机进行自行设置位置和方向。调整期间飞机之间可以重叠。但是在设置结束时需要判断是否满足不重叠条件。不满足时提示不可重叠。
    private int moveAirplaneNo = 0;//移动的飞机在集合中的位置
    private Airplane moveAirplane;//移动的飞机
    private float moveStartX;//移动起始的x坐标
    private float moveStartY;//移动起始的y坐标
    private long timestamp;//时间戳，用来判断手指是否是单击

    public BombView(Context context) {
        this(context, null);
    }

    public BombView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    /**
     * 初始化
     */
    private void init() {
        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setDither(true);
        paint.setStyle(Paint.Style.FILL_AND_STROKE);
        paint.setColor(colorBlack);

        textPaint = new TextPaint();
        textPaint.setAntiAlias(true);
        textPaint.setDither(true);
        textPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        textPaint.setColor(colorBlack);
        textPaint.setTextSize(UI.getPx(R.dimen.wpx40));

        rect = new Rect();
        random = new Random();

        autoSet();
    }

    /**
     * 初始化格子
     */
    private void initBlocks() {
        arrBlocks = new Block[10][10];
        for (int i = 0; i < 10; i++) {
            for (int j = 0; j < 10; j++) {
                Block block = new Block(i, j);
                block.setText("空");
                if (Checker.hasList(listAirplanes)) {
                    for (int k = 0; k < listAirplanes.size(); k++) {
                        Airplane airplane = listAirplanes.get(k);
                        List<Block> blockList = airplane.getList();
                        if (Checker.hasList(blockList)) {
                            for (int l = 0; l < blockList.size(); l++) {
                                if (blockList.get(l).equals(block)) {
                                    if (l == 0) {
                                        block.setText("沉");
                                    } else {
                                        block.setText("伤");
                                    }
                                }
                            }
                        }
                    }
                }
                arrBlocks[i][j] = block;
            }
        }
    }

    /**
     * 设置开启编辑模式
     *
     * @return 是否成功
     */
    public boolean setEditMode() {
        if (countDead >= 3) {
            ToastUtils.getInstance().showInfo("请重新开始游戏");
            return false;
        }
        editMode = true;
        showOrHide(true);
        postInvalidate();
        return true;
    }

    /**
     * 自动设置飞机
     */
    public void autoSet() {
        if (Checker.hasList(listAirplanes)) {
            listAirplanes.clear();
        }
        listAirplanes = new ArrayList<>();
        countCreate = 0;
        step = 0;
        countDead = 0;
        while (listAirplanes.size() < 3) {
            int x = random.nextInt(10);
            int y = random.nextInt(10);
            int direction = random.nextInt(4);//随机方向
            Airplane newAirplane = getAirplane(x, y, direction, true);
            if (Checker.notNull(newAirplane)) {
                if (Checker.hasList(listAirplanes)) {
                    boolean noUseRepeatBlock = true;//是否未使用重复格子（不允许有重复的格子）
                    for (int i = 0; i < listAirplanes.size(); i++) {
                        Airplane temp = listAirplanes.get(i);
                        if (!noUseRepeatBlock(temp, newAirplane)) {
                            noUseRepeatBlock = false;
                        }
                    }
                    if (noUseRepeatBlock) {
                        listAirplanes.add(newAirplane);
                    }
                } else {
                    listAirplanes.add(newAirplane);
                }
            }
        }
        LogUtils.e("生成题目共生成飞机count=" + countCreate);
        initBlocks();
        postInvalidate();
    }

    /**
     * 显示所有飞机
     */
    public void show() {
        if (countDead >= 3) {
            ToastUtils.getInstance().showInfo("请重新开始游戏");
            return;
        }
        showOrHide(true);
    }

    /**
     * 编辑结束，判断是否满足条件
     *
     * @return 是否满足条件
     */
    public boolean editComplete() {
        if (countDead >= 3) {
            ToastUtils.getInstance().showInfo("请重新开始游戏");
            return false;
        }
        if (editMode) {
            initBlocks();
            //检查三个飞机是否完整，并且无重叠。直接计算飞机的不重复的总格子数是否达到30。不到30肯定是有格子重叠或者飞机格子缺少。
            //发现一个更加简单的方法，算空白格子数即可。100-空白=飞机占的格子总数。
            int kongNum = 0;
            if (Checker.notNull(arrBlocks)) {
                for (int i = 0; i < 10; i++) {
                    for (int j = 0; j < 10; j++) {
                        Block block = arrBlocks[i][j];
                        if (block.getText().equals("空")) {
                            kongNum++;
                        }
                    }
                }
            }
            if (100 - kongNum != 30) {
                ToastUtils.getInstance().showInfo("飞机有重叠或者在棋盘外，请调整");
                return false;
            }
        }
        showOrHide(false);
        editMode = false;
        return true;
    }

    /**
     * 隐藏所有飞机
     */
    public void hide() {
        if (countDead >= 3) {
            ToastUtils.getInstance().showInfo("请重新开始游戏");
            return;
        }
        if (editMode) {
            ToastUtils.getInstance().showInfo("请先结束调整");
            return;
        }
        showOrHide(false);
        editMode = false;
    }

    /**
     * 显示或隐藏全部三架飞机
     */
    private void showOrHide(boolean show) {
        if (Checker.hasList(listAirplanes)) {
            for (int i = 0; i < listAirplanes.size(); i++) {
                Airplane airplane = listAirplanes.get(i);
                List<Block> blockList = airplane.getList();
                if (Checker.hasList(blockList)) {
                    for (int j = 0; j < blockList.size(); j++) {
                        blockList.get(j).setShow(show);
                    }
                }
            }
        }
        postInvalidate();
    }

    /**
     * 是否没有使用到重复的格子
     *
     * @param ori    原飞机
     * @param target 目标飞机
     * @return 所求
     */
    private boolean noUseRepeatBlock(Airplane ori, Airplane target) {
        boolean noCover = true;
        List<Block> listOri = ori.getList();
        if (Checker.hasList(listOri) && Checker.hasList(target.getList())) {
            for (int i = 0; i < listOri.size(); i++) {
                if (target.getList().contains(listOri.get(i))) {
                    noCover = false;
                    break;
                }
            }
        }
        return noCover;
    }

    /**
     * 根据机头的坐标为执随机获取一个随机方向的飞机的对象
     *
     * @param headX     机头的x坐标
     * @param headY     机头的y坐标
     * @param direction 机尾的方向
     * @param whole     整个机身无残缺，在旋转时可残缺
     * @return 所求
     */
    private Airplane getAirplane(int headX, int headY, int direction, boolean whole) {
        countCreate += 1;
        List<Block> list = new ArrayList<>();
        list.add(new Block(headX, headY));//飞机头的格子放在集合的第一个
        direction = direction % directions.length;//加上这句后，方法外的direction可以传入高于4的值
        int tryDirection = direction;//从随机方向开始尝试
        out:
        for (int i = 0; i < 4; i++) {
            switch (directions[tryDirection]) {
                case DIRECTION_LEFT://机尾朝向左边
                    if (whole && (headX - 3 < 0 || headY - 2 < 0 || headY + 2 > 9)) {//根据飞机4*5或者5*4的规格，判断超出棋盘则进行下一次尝试
                        tryDirection = (tryDirection + 1) % directions.length;
                        if (tryDirection == direction) break;//如果四个方向都试完都不行，则直接跳出循环
                        continue;
                    }
                    //飞机头外的剩下的格子
                    list.add(new Block(headX - 1, headY - 2));
                    list.add(new Block(headX - 1, headY - 1));
                    list.add(new Block(headX - 1, headY));
                    list.add(new Block(headX - 1, headY + 1));
                    list.add(new Block(headX - 1, headY + 2));
                    list.add(new Block(headX - 2, headY));
                    list.add(new Block(headX - 3, headY - 1));
                    list.add(new Block(headX - 3, headY));
                    list.add(new Block(headX - 3, headY + 1));
                    break out;
                case DIRECTION_TOP:
                    if (whole && (headY - 3 < 0 || headX - 2 < 0 || headX + 2 > 9)) {
                        tryDirection = (tryDirection + 1) % directions.length;
                        if (tryDirection == direction) break;
                        continue;
                    }
                    list.add(new Block(headX - 2, headY - 1));
                    list.add(new Block(headX - 1, headY - 1));
                    list.add(new Block(headX, headY - 1));
                    list.add(new Block(headX + 1, headY - 1));
                    list.add(new Block(headX + 2, headY - 1));
                    list.add(new Block(headX, headY - 2));
                    list.add(new Block(headX - 1, headY - 3));
                    list.add(new Block(headX, headY - 3));
                    list.add(new Block(headX + 1, headY - 3));
                    break out;
                case DIRECTION_RIGHT:
                    if (whole && (headX + 3 > 9 || headY - 2 < 0 || headY + 2 > 9)) {
                        tryDirection = (tryDirection + 1) % directions.length;
                        if (tryDirection == direction) break;
                        continue;
                    }
                    list.add(new Block(headX + 1, headY - 2));
                    list.add(new Block(headX + 1, headY - 1));
                    list.add(new Block(headX + 1, headY));
                    list.add(new Block(headX + 1, headY + 1));
                    list.add(new Block(headX + 1, headY + 2));
                    list.add(new Block(headX + 2, headY));
                    list.add(new Block(headX + 3, headY - 1));
                    list.add(new Block(headX + 3, headY));
                    list.add(new Block(headX + 3, headY + 1));
                    break out;
                case DIRECTION_DOWN:
                    if (whole && (headY + 3 > 9 || headX - 2 < 0 || headX + 2 > 9)) {
                        tryDirection = (tryDirection + 1) % directions.length;
                        if (tryDirection == direction) break;
                        continue;
                    }
                    list.add(new Block(headX - 2, headY + 1));
                    list.add(new Block(headX - 1, headY + 1));
                    list.add(new Block(headX, headY + 1));
                    list.add(new Block(headX + 1, headY + 1));
                    list.add(new Block(headX + 2, headY + 1));
                    list.add(new Block(headX, headY + 2));
                    list.add(new Block(headX - 1, headY + 3));
                    list.add(new Block(headX, headY + 3));
                    list.add(new Block(headX + 1, headY + 3));
                    break out;
                default:
            }
        }
        if (Checker.hasList(list) && list.size() == 10) {
            return new Airplane(list, tryDirection);
        } else {
            return null;
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawColor(colorWhite);
        if (blockLong <= 0) {
            blockLong = getWidth() / 11f;
        }
        //lines
        paint.setColor(colorGray);
        for (int i = 0; i < 11; i++) {
            //rows
            canvas.drawLine(blockLong, blockLong * (i + 1), getWidth(), blockLong * (i + 1), paint);
            //columns
            canvas.drawLine(blockLong * (i + 1), blockLong, blockLong * (i + 1), getHeight(), paint);
        }
        //tags
        textPaint.setColor(colorBlack);
        for (int i = 0; i < rows.length; i++) {
            //rows
            textPaint.getTextBounds(rows[i], 0, rows[i].length(), rect);
            canvas.drawText(rows[i], blockLong / 2f - rect.width() / 2f, blockLong * (i + 1) + blockLong / 2f - (rect.bottom + rect.top) / 2f, textPaint);
            //columns
            String strColumn = String.valueOf(i + 1);
            textPaint.getTextBounds(strColumn, 0, strColumn.length(), rect);
            canvas.drawText(strColumn, blockLong * (i + 1) + blockLong / 2f - rect.width() / 2f, blockLong / 2f - (rect.bottom + rect.top) / 2f, textPaint);
        }
        //text
        for (Block[] arrBlock : arrBlocks) {
            for (int j = 0; j < arrBlocks[0].length; j++) {
                Block block = arrBlock[j];
                if (block.isShow()) {
                    String text = block.getText();
                    textPaint.getTextBounds(text, 0, text.length(), rect);
                    paint.setColor(text.equals("沉") ? colorDead : text.equals("伤") ? colorHurt : colorNull);
                    canvas.drawRect((block.getX() + 1) * blockLong + 1 / 2f, (block.getY() + 1) * blockLong + 1 / 2f, (block.getX() + 2) * blockLong - 1 / 2f, (block.getY() + 2) * blockLong - 1 / 2f, paint);
                    canvas.drawText(text, (block.getX() + 1) * blockLong + blockLong / 2f - rect.width() / 2f, (block.getY() + 1) * blockLong + blockLong / 2f - (rect.bottom + rect.top) / 2f, textPaint);
                }
            }
        }
        //airplanes
        if (Checker.hasList(listAirplanes)) {
            for (int i = 0; i < listAirplanes.size(); i++) {
                Airplane airplane = listAirplanes.get(i);
                List<Block> blockList = airplane.getList();
                paint.setColor(i == 0 ? colorAirplane1 : i == 1 ? colorAirplane2 : colorAirplane3);
                if (Checker.hasList(blockList)) {
                    for (int j = 0; j < blockList.size(); j++) {
                        Block block = blockList.get(j);
                        if (block.isShow())
                            canvas.drawRect((block.getX() + 1) * blockLong, (block.getY() + 1) * blockLong, (block.getX() + 2) * blockLong, (block.getY() + 2) * blockLong, paint);
                    }
                }
            }
        }
        //step
        String str = step + "步";
        textPaint.getTextBounds(str, 0, str.length(), rect);
        canvas.drawText(str, blockLong / 2f - rect.width() / 2f, blockLong / 2f - (rect.top + rect.bottom) / 2f, textPaint);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float x = event.getX();
        float y = event.getY();
        int action = event.getAction();
        int blockX, blockY;
        blockX = (int) ((x - blockLong) / blockLong);
        blockY = (int) ((y - blockLong) / blockLong);
        if (editMode) {
            //编辑时点击飞机任意部分皆可选中飞机，然后拖动设置飞机位置
            switch (action) {
                case MotionEvent.ACTION_DOWN:
                    moveAirplaneNo = -1;
                    moveStartX = x;
                    moveStartY = y;
                    //选中了飞机，找出选中的是
                    if (Checker.hasList(listAirplanes)) {
                        out:
                        for (int i = 0; i < listAirplanes.size(); i++) {
                            Airplane airplane = listAirplanes.get(i);
                            if (Checker.hasList(airplane.getList())) {
                                List<Block> blockList = airplane.getList();
                                for (int j = 0; j < blockList.size(); j++) {
                                    Block b = blockList.get(j);
                                    if (b.getX() == blockX && b.getY() == blockY) {//表示选中了该飞机
                                        moveAirplaneNo = i;
                                        moveAirplane = new Airplane(airplane.getList(), airplane.getDirection());
                                        break out;
                                    }
                                }
                            }
                        }
                    }
                    return true;
                case MotionEvent.ACTION_MOVE: {
                    int deltaX = (int) ((x - moveStartX) / blockLong);
                    int deltaY = (int) ((y - moveStartY) / blockLong);
                    if (deltaX != 0) moveStartX = x;
                    if (deltaY != 0) moveStartY = y;
                    if (moveAirplaneNo > -1 && (deltaX != 0 || deltaY != 0)) {
                        if (Checker.hasList(listAirplanes) && Checker.notNull(listAirplanes.get(moveAirplaneNo)) && Checker.notNull(moveAirplane)) {
                            Airplane airplane = listAirplanes.get(moveAirplaneNo);
                            if (Checker.hasList(airplane.getList()) && Checker.hasList(moveAirplane.getList())) {
                                List<Block> blockList = airplane.getList();
                                List<Block> blockListOri = moveAirplane.getList();
                                for (int j = 0; j < blockList.size(); j++) {
                                    Block b = blockList.get(j);
                                    Block bOri = blockListOri.get(j);
                                    b.setX(bOri.getX() + deltaX);
                                    b.setY(bOri.getY() + deltaY);
                                }
                            }
                        }
                        postInvalidate();
                    }
                    return true;
                }
                case MotionEvent.ACTION_UP:
                    long now = System.currentTimeMillis();
                    boolean isRotate = now - timestamp < 200;//双击旋转飞机
                    timestamp = now;
                    //将飞机进行旋转
                    if (isRotate && Checker.hasList(listAirplanes) && moveAirplaneNo > -1 && listAirplanes.size() > moveAirplaneNo) {
                        Airplane airplane = listAirplanes.get(moveAirplaneNo);
                        Block head = airplane.getList().get(0);
                        if (Checker.notNull(head)) {
                            Airplane newOne = getAirplane(head.getX(), head.getY(), airplane.getDirection() + 1, false);//加一表示替换成下一个方向
                            if (Checker.notNull(newOne)) {
                                //显示旋转后的飞机
                                List<Block> blockList = newOne.getList();
                                if (Checker.hasList(blockList)) {
                                    for (int i = 0; i < blockList.size(); i++) {
                                        blockList.get(i).setShow(true);
                                    }
                                }
                                listAirplanes.set(moveAirplaneNo, newOne);
                                postInvalidate();
                            }
                        }
                    }
                    return true;
            }
        } else {
            //以下三行是编辑模式剩下的，需要重置一下值
            moveAirplaneNo = -1;
            moveStartX = 0;
            moveStartY = 0;
            //游戏中点击格子显示是否点中机身
            switch (action) {
                case MotionEvent.ACTION_DOWN:

                    return true;
                case MotionEvent.ACTION_UP:
                    if (countDead >= 3) {
                        ToastUtils.getInstance().showInfo("请重新开始游戏");
                        return true;
                    }
//                    LogUtils.e("点击的格子坐标为 x=" + blockX + " y=" + blockY);
                    Block block = arrBlocks[blockX][blockY];
                    if (!block.isShow() && countDead < 3) {
                        if (block.getText().equals("沉")) {
                            if (Checker.hasList(listAirplanes)) {
                                for (int i = 0; i < listAirplanes.size(); i++) {
                                    Airplane airplane = listAirplanes.get(i);
                                    List<Block> blockList = airplane.getList();
                                    if (Checker.hasList(blockList)) {
                                        Block head = blockList.get(0);
                                        if (block.equals(head)) {
                                            for (int j = 0; j < blockList.size(); j++) {
                                                blockList.get(j).setShow(true);
                                            }
                                            countDead++;
                                            break;
                                        }
                                    }
                                }
                            }
                        }
                        if (countDead >= 3) {
                            ToastUtils.getInstance().showInfo("恭喜胜利");
                        }
                        step++;
                        block.setShow(true);
                        postInvalidate();
                    }
                    return true;
            }
        }
        return super.onTouchEvent(event);
    }
}
